package com.liyunc.demo.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.liyunc.demo.pojo.UserInfo;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义token生成工具
 * Header.Payload.Signature
 */
@Component
public class JwtUtil {

    private static final String SECRET =
        "qXmfHZqWOR4W6cSt2HJWvOSKDb8O0ivabqbpFR65MQFxPqWtjJF4nWW8UwdYABAi"
            + "yv8fzxTYxaxbYQQ8JlFve9bXDEbVIzLqTF1OqGtMdPMEJ4yBGStzAPcmyHCAyHtv"
            + "0KGdSFMS8zifBcbrBVzJ0T6cyG9dQvUSYlCqvBdF680mmnY9k6DpR1zgbdjMroHk"
            + "BRfxBbvzlJSck6rPMm0tx13WVAIez0lVTp2A4384c57oMUjoqNNp1ggDYFS43J6e"
            + "D5WvAiRa9lydLXj5ZX7n7Qsrylbfrw5erswgGoTktGjhfa5P7ymkfD85ppiIYKb6"
            + "TFZbt3NlQwFOqFX3Zt0AmouCOqjhjGOuSwzqkeY5rOUDNw33KgDTjBk1lb2tkmd9"
            + "ocKsdcMRxvan9jHXAkIyFkaHpHOf4hgKJQsvUUCNlNMy5ujS6Szn8OSCJoqZcV7c"
            + "ef5T9AW53qLn0xxClKtlhkTXDrJKal1iyUqkT7iuv8u0g7WNsnaD7ldIwSjrf6Hb"
            + "Ik2uePmv2jEzj6iakoZisCcdt8ktL6x8oFtYhzBoJqb0jto5SwSTwmFowJBOXRhm"
            + "kqrKc9fXzGU2SSOX5ZDx6uF0MoTTkf8JKDH9hChLM1R97FDBCs3WFNIqOIVBZIxR"
            + "oJ3HItiCwWiDMTfzQl76H59cy4kctyebPzN7FiFI8tKkdj8eZ0dw5BfmeFw2xez5"
            + "FFURUjElFB3u3ershE6rUFK0GU3mZtFRIakM68q48oSX0AfpV1pRljVzo1xP6SGY"
            + "N3T9qOh3IMm7qpEWchab0PQN8WxXg4GrjcKu0n12CtEbTnPSkSjkKGCempGC0hVa"
            + "0bh79vDI9a3aq1A9b3AnoMhOVvg7PcEFE9ZBeJqkvcgvvZhut9QNsy0YXPTRGcpY"
            + "8XDVKWVfnVKAZ6ZiDlcr7Ss6hZaTRKD2dc60X7SndLPai929RUDIJ24LkA96hNLm"
            + "lFsHEGW0QQK25xYLPeUS5sxgxMfAzS37fAFq0RtseeHDLOflS0tZipnyhGmHXES7";
    //过期时间:秒
    private static final int EXPIRE = 2 * 60 * 60 * 1000;

    //此处用map集合模拟比如用redis来存储token
    private static final Map<String, Object> TOKEN_MAP = new ConcurrentHashMap<>();


    /**
     * 生成Token.
     * 注意线程安全问题.
     */
    public static synchronized String createToken(String username, String authorities)
        throws Exception {
        synchronized (JwtUtil.class) {
            //设置过期时间
            Date nowDate = new Date();
            Date expireDate = new Date(nowDate.getTime() + EXPIRE);

            /*
             * token header
             * Header 部分是一个 JSON 对象，描述 JWT 的元数据，通常是下面的样子。
             * {
             *   "alg": "HS256",
             *   "typ": "JWT"
             * }
             */
            Map<String, Object> map = new HashMap<>();
            map.put("alg", "HS256");
            map.put("typ", "JWT");

            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            String token = JWT.create()
                .withHeader(map)//header
                .withClaim("authorities", authorities)
                .withSubject(username)//主题
                .withIssuer("auth0")//签发人
                .withIssuedAt(nowDate)//签名时间
                .withExpiresAt(expireDate)//过期时间
                .sign(algorithm);//签名

            //存入map中
            TOKEN_MAP.put("token", token);

            return token;
        }
    }

    /**
     * 验证Token.
     */
    public static synchronized boolean validateToken(
        String tokenKey, UserDetails userDetails) {
        try {
            String tokenValue = (String) TOKEN_MAP.get(tokenKey);
            UserInfo userInfo = (UserInfo) userDetails;
            if (StringUtils.isEmpty(tokenValue) || null == userInfo) {
                return false;
            }
            Map<String, Claim> claimMap = parse(tokenKey);
            return userInfo.getUsername().equals(claimMap.get("sub").asString())
                && isExpired(tokenKey);
        } catch (Exception exception) {
            return false;
        } finally {
            //保证每个接口对应的token只能访问一次，保证接口幂等性问题
            TOKEN_MAP.remove(tokenKey);
        }
    }

    /**
     * 解析Token.
     */
    private static synchronized Map<String, Claim> parse(String token) throws Exception {
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
        DecodedJWT jwt;
        try {
            jwt = verifier.verify(token);
        } catch (Exception exception) {
            throw new RuntimeException("凭证已过期，请重新登录");
        }
        return jwt.getClaims();
    }

    /**
     * 解析Token.
     */
    public static Map<String, String> parseToken(String token) throws Exception {
        Map<String, String> data = new HashMap<>();
        Map<String, Claim> claimMap = parse(token);
        data.put("sub", claimMap.get("sub").asString());
        data.put("iss", claimMap.get("iss").asString());
        data.put("exp", claimMap.get("exp").asDate().toString());
        data.put("iat", claimMap.get("iat").asDate().toString());
        data.put("authorities", claimMap.get("authorities").asString());
        return data;
    }

    /**
     * token是否过期.
     *
     * @return true:过期 false:没有过期
     */
    public static boolean isExpired(String token) {
        if (null == token) {
            return true;
        }
        try {
            Map<String, Claim> claimMap = parse(token);
            Date exp = claimMap.get("exp").asDate();
            return exp.before(new Date());
        } catch (Exception exception) {
            return true;
        }
    }
}
